package cn.richard.demo.ruleengine.web;

import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.api.RulesEngineParameters;
import org.jeasy.rules.core.AbstractRulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.mvel.MVELRule;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.hutool.core.collection.CollUtil;
import cn.richard.demo.ruleengine.bean.ExTransDto;
import cn.richard.demo.ruleengine.bean.Person;
import cn.richard.demo.ruleengine.config.MyRuleListener;
import cn.richard.demo.ruleengine.config.MyRulesEngineListener;
import cn.richard.demo.ruleengine.config.NacosConfigExChannelRule;
import cn.richard.demo.ruleengine.config.NacosYmlAppObjBean;
import cn.richard.demo.ruleengine.config.RuleFireService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class TestCtrller {
	@Value("${app.name}")
	private String app_name;
	
	@Value("${server.port}")
	private int port;
	
	@Resource
	private NacosYmlAppObjBean ymlObjBean;
	@Resource
	private NacosConfigExChannelRule exChannelRuleConfig;
	
	@Resource
	private RuleFireService ruleFireService;
	

	@RequestMapping("/hello")
	public String hello() {
		return LocalDateTime.now().toString();
	}
	
	@RequestMapping("/test1")
	public String test1() {
		return app_name;
	}
	
	@RequestMapping("/test2")
	public String test2() {
		return ymlObjBean.toString();
	}
	
	@RequestMapping("/test3")
	public Object test3() {
		return exChannelRuleConfig;
	}
	
	//快速测试主要功能
	@RequestMapping("/testRule0")
	public Object testRule(Person person) {
		//create a person instance (fact)
        Facts facts = new Facts();
        facts.put("person", person);
//        log.info("facts before fire: {}", facts.asMap());

        // create rules
        Rules rules = new Rules();
        /**
         * MVEL2语法：
         * 	http://mvel.documentnode.com/
         * 	https://bigjun2017.github.io/2018/09/18/hou-duan/java/mvel2.x-yu-fa-zhi-nan/
         * SPEL语法：
         * 	https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions
         */
        exChannelRuleConfig.getRules().forEach(o -> {
        	rules.register(new MVELRule().name(o.getName())
        								 .description(o.getDescription())
        								 .priority(o.getPriority())
        								 .when(o.getCondition())
        								 .then(getActions(o.getActions())));
        });
        log.info("rules size: {}", getRulesCount(rules));
        
        /**
         * DefaultRulesEngine：根据规则的自然顺序（默认情况下为优先级）应用规则。
         * InferenceRulesEngine：持续将规则应用于已知事实，直到不再适用任何规则为止。
         * 	引擎的几个设置参数：
         * 		skipOnFirstAppliedRule参数 - 引擎应用规则后跳过后面的规则。即 matchAnyOne。
         * 		skipOnFirstFailedRule参数 - 引擎在规则失败时跳过后面的规则。
         * 		skipOnFirstNonTriggeredRule参数 - 引擎在未触发规则时跳过后面的规则。失败和未触发有待区分。
         * 		rulePriorityThreshold参数 - 如果优先级超过定义的阈值跳过后面的规则。
         */
        RulesEngineParameters parameters = new RulesEngineParameters()
        		//.priorityThreshold(10)
        		//.skipOnFirstFailedRule(true)
        		//.skipOnFirstNonTriggeredRule(true)
		        //.skipOnFirstAppliedRule(true)
		        ;
        //create a default rules engine and fire rules on known facts
        RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
        //rulesEngine = new InferenceRulesEngine();

        /**
         * RuleListener 
         * 	定义并注册规则监听器，可以对每个规则进行事件监听。您可以实现此接口以提供自定义行为，以在每个规则之前/之后执行。
         *  建议：可对 onEvaluationError 和 onFailure 进行日志和报警，对 onSuccess 进行日志。
         */
        ((AbstractRulesEngine) rulesEngine).registerRuleListener(new MyRuleListener());
        
        /**
         * RulesEngineListener 比 RuleListener 粒度大一些。
         * 与为每个规则执行的RuleListener不同，RulesEngineListener允许您在触发整个规则集之前/之后提供自定义行为。
         */
        ((AbstractRulesEngine) rulesEngine).registerRulesEngineListener(new MyRulesEngineListener());
        
        System.out.println(">> Hi! can I have some Vodka please ?");
        rulesEngine.fire(rules, facts);
//        log.info("facts after fire: {}", facts.asMap());
		return exChannelRuleConfig;
	}
	
	//易用性改进版
	@RequestMapping("/testRule1")
	public Object testRule1(Person person) {
		ruleFireService.fire("person", person);
		return person;
	}
	
	//主动刷新
	@RequestMapping("/testRefreshRules")
	public Object testRefreshRules() {
		ruleFireService.refreshRules();
		return "ok";
	}
	
	
	//业务测试，多维度决策渠道
	@RequestMapping("/testExRule")
	public Object testRuleEx(ExTransDto dto) {
		ruleFireService.fire("trans", dto);
		return dto;
	}
	
	private String getActions(List<String> actions) {
		StringBuilder sb = new StringBuilder();
		if(CollUtil.isNotEmpty(actions)) {
			actions.forEach(sb::append);
		}
		return sb.toString();
	}
	
	private int getRulesCount(Rules rules) {
		int count = 0;
		Iterator<Rule> iterator = rules.iterator();
		while(iterator.hasNext()) {
			Rule rule = iterator.next();
			//System.out.println(rule);
			count+=1;
		}
		return count;
	}
}
